﻿using Newtonsoft.Json;
using PMS.Core.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PMS.Services.Plugins
{
    /// <summary>
    /// Represents an information about plugins
    /// </summary>
    public partial class PluginsInfo : IPluginsInfo
    {
        #region Fields

        private IList<PluginDescriptorBaseInfo> _installedPlugins = new List<PluginDescriptorBaseInfo>();

        protected readonly IPMSFileProvider _fileProvider;

        #endregion

        #region Utilities

        /// <summary>
        /// Deserialize PluginInfo from json
        /// </summary>
        /// <param name="json">Json data of PluginInfo</param>
        /// <returns>True if data are loaded, otherwise False</returns>
        protected virtual bool DeserializePluginInfo(string json)
        {
            var pluginsInfo = JsonConvert.DeserializeObject<PluginsInfo>(json);

            InstalledPlugins = pluginsInfo.InstalledPlugins;
            PluginNamesToUninstall = pluginsInfo.PluginNamesToUninstall;
            PluginNamesToDelete = pluginsInfo.PluginNamesToDelete;
            PluginNamesToInstall = pluginsInfo.PluginNamesToInstall;

            return InstalledPlugins.Any() || PluginNamesToUninstall.Any() || PluginNamesToDelete.Any() ||
                   PluginNamesToInstall.Any();
        }

        #endregion

        #region Ctor

        public PluginsInfo(IPMSFileProvider fileProvider)
        {
            _fileProvider = fileProvider;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Save plugins info to the file
        /// </summary>
        public virtual void Save()
        {
            //save the file
            var filePath = _fileProvider.MapPath(PluginDefaultConfigs.PluginsInfoFilePath);
            var text = JsonConvert.SerializeObject(this, Formatting.Indented);
            _fileProvider.WriteAllText(filePath, text, Encoding.UTF8);
        }

        /// <summary>
        /// Get plugins info
        /// </summary>
        /// <returns>True if data are loaded, otherwise False</returns>
        public virtual bool LoadPluginInfo()
        {
            //check whether plugins info file exists
            var filePath = _fileProvider.MapPath(PluginDefaultConfigs.PluginsInfoFilePath);
            
            //try to get plugin info from the JSON file
            var text = _fileProvider.FileExists(filePath)
                ? _fileProvider.ReadAllText(filePath, Encoding.UTF8)
                : string.Empty;
            return !string.IsNullOrEmpty(text) && DeserializePluginInfo(text);
        }

        /// <summary>
        /// Create copy from another instance of IPluginsInfo interface
        /// </summary>
        /// <param name="pluginsInfo">Plugins info</param>
        public virtual void CopyFrom(IPluginsInfo pluginsInfo)
        {
            InstalledPlugins = pluginsInfo.InstalledPlugins?.ToList() ?? new List<PluginDescriptorBaseInfo>();
            PluginNamesToUninstall = pluginsInfo.PluginNamesToUninstall?.ToList() ?? new List<string>();
            PluginNamesToDelete = pluginsInfo.PluginNamesToDelete?.ToList() ?? new List<string>();
            PluginNamesToInstall = pluginsInfo.PluginNamesToInstall?.ToList() ??
                                   new List<(string SystemName, Guid? CustomerGuid)>();
            AssemblyLoadedCollision = pluginsInfo.AssemblyLoadedCollision?.ToList();
            PluginDescriptors = pluginsInfo.PluginDescriptors?.ToList();
            IncompatiblePlugins = pluginsInfo.IncompatiblePlugins?.ToList();
        }

        #endregion

        #region Properties

        #region Nested_Class_of_Comapre
        private class PluginDescriptorBaseInfoComparer : IEqualityComparer<PluginDescriptorBaseInfo>
        {
            public bool Equals(PluginDescriptorBaseInfo x, PluginDescriptorBaseInfo y)
            {
                //First check if both object reference are equal then return true
                if (object.ReferenceEquals(x, y))
                {
                    return true;
                }
                //If either one of the object refernce is null, return false
                if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
                {
                    return false;
                }
                //Comparing all the properties one by one
                return x.SystemName.Equals(y.SystemName, StringComparison.OrdinalIgnoreCase) && x.Version.Equals(y.Version, StringComparison.OrdinalIgnoreCase);
            }
            public int GetHashCode(PluginDescriptorBaseInfo obj)
            {
                //If obj is null then return 0
                if (obj == null)
                {
                    return 0;
                }
                //Get the SystemName hash code value
                int NameHashCode = obj.SystemName.GetHashCode();
                //Get the string HashCode Value
                //Check for null refernece exception
                int VersionHashCode = obj.SystemName == null ? 0 : obj.SystemName.GetHashCode();
                return NameHashCode ^ VersionHashCode;
            }
        }
        #endregion
        /// <summary>
        /// Gets or sets the list of all installed plugin
        /// </summary>
        public virtual IList<PluginDescriptorBaseInfo> InstalledPlugins
        {
            get
            {
                if (_installedPlugins?.Any() ?? false)
                    //DavidLee 增加过滤器过滤PluginNamesToInstall安装组件时重复的相同组件
                    return _installedPlugins.Distinct(new PluginDescriptorBaseInfoComparer()).ToList();

                if (PluginDescriptors?.Any() ?? false)
                    _installedPlugins = PluginDescriptors
                        .Select(pd => pd as PluginDescriptorBaseInfo).ToList();

                return _installedPlugins;
            }
            set => _installedPlugins = value;
        }

        /// <summary>
        /// Gets or sets the list of plugin names which will be uninstalled
        /// </summary>
        public virtual IList<string> PluginNamesToUninstall { get; set; } = new List<string>();

        /// <summary>
        /// Gets or sets the list of plugin names which will be deleted
        /// </summary>
        public virtual IList<string> PluginNamesToDelete { get; set; } = new List<string>();

        /// <summary>
        /// Gets or sets the list of plugin names which will be installed
        /// DavidLee 2020/11/12 Tuple types:
        /// see https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples
        /// </summary>
        public virtual IList<(string SystemName, Guid? CustomerGuid)> PluginNamesToInstall { get; set; } =
            new List<(string SystemName, Guid? CustomerGuid)>();

        /// <summary>
        /// Gets or sets the list of plugin names which are not compatible with the current version
        /// </summary>
        [JsonIgnore]
        public virtual IList<string> IncompatiblePlugins { get; set; }

        /// <summary>
        /// Gets or sets the list of assembly loaded collisions
        /// </summary>
        [JsonIgnore]
        public virtual IList<PluginLoadedAssemblyInfo> AssemblyLoadedCollision { get; set; }

        /// <summary>
        /// Gets or sets a collection of plugin descriptors of all deployed plugins
        /// </summary>
        [JsonIgnore]
        public virtual IList<PluginDescriptor> PluginDescriptors { get; set; }

        #endregion
    }
}